Skip to content

[clang-doc] Extract Info into JSON values #138063

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2025

Conversation

ilovepi
Copy link
Contributor

@ilovepi ilovepi commented May 1, 2025

Split from #133161. This patch provides the implementation of a number
of extractValue overloads used with the different types of Info.

The new helper functions extract the relevant information from the
different *Infos and inserts them into the correct fields of the JSON
values that will be used with the specific Mustache templates, which
will land separately.

Co-authored-by: Peter Chou [email protected]

@llvmbot
Copy link
Member

llvmbot commented May 1, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Paul Kirth (ilovepi)

Changes

Split from #133161. This patch provides the implementation of a number
of extractValue overloads used with the different types of Info.

The new helper functions extract the relevant information from the
different *Infos and inserts them into the correct fields of the JSON
values that will be used with the specific Mustache templates, which
will land separately.

Co-authored-by: Peter Chou <[email protected]>


Full diff: https://github.com/llvm/llvm-project/pull/138063.diff

1 Files Affected:

  • (modified) clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp (+253-2)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 593d5d1221f44..29392f8bf17b9 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -141,21 +141,272 @@ Error MustacheHTMLGenerator::generateDocs(
   return Error::success();
 }
 
+static json::Value
+extractValue(const Location &L,
+             std::optional<StringRef> RepositoryUrl = std::nullopt) {
+  Object Obj = Object();
+  // Should there be Start/End line numbers?
+  Obj.insert({"LineNumber", L.StartLineNumber});
+  Obj.insert({"Filename", L.Filename});
+
+  if (!L.IsFileInRootDir || !RepositoryUrl) {
+    return Obj;
+  }
+  SmallString<128> FileURL(*RepositoryUrl);
+  sys::path::append(FileURL, sys::path::Style::posix, L.Filename);
+  FileURL += "#" + std::to_string(L.StartLineNumber);
+  Obj.insert({"FileURL", FileURL});
+
+  return Obj;
+}
+
+static json::Value extractValue(const Reference &I,
+                                StringRef CurrentDirectory) {
+  SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
+  sys::path::append(Path, I.getFileBaseName() + ".html");
+  sys::path::native(Path, sys::path::Style::posix);
+  Object Obj = Object();
+  Obj.insert({"Link", Path});
+  Obj.insert({"Name", I.Name});
+  Obj.insert({"QualName", I.QualName});
+  Obj.insert({"ID", toHex(toStringRef(I.USR))});
+  return Obj;
+}
+
+static json::Value extractValue(const TypedefInfo &I) {
+  // Not Supported
+  return nullptr;
+}
+
+static json::Value extractValue(const CommentInfo &I) {
+  assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" ||
+          I.Kind == "ParagraphComment" || I.Kind == "TextComment") &&
+         "Unknown Comment type in CommentInfo.");
+
+  Object Obj = Object();
+  json::Value Child = Object();
+
+  // TextComment has no children, so return it.
+  if (I.Kind == "TextComment") {
+    Obj.insert({"TextComment", I.Text});
+    return Obj;
+  }
+
+  // BlockCommandComment needs to generate a Command key.
+  if (I.Kind == "BlockCommandComment") {
+    Child.getAsObject()->insert({"Command", I.Name});
+  }
+
+  // Use the same handling for everything else.
+  // Only valid for:
+  //  - BlockCommandComment
+  //  - FullComment
+  //  - ParagraphComment
+  json::Value ChildArr = Array();
+  auto &CARef = *ChildArr.getAsArray();
+  CARef.reserve(I.Children.size());
+  for (const auto &C : I.Children)
+    CARef.emplace_back(extractValue(*C));
+  Child.getAsObject()->insert({"Children", ChildArr});
+  Obj.insert({I.Kind, Child});
+
+  return Obj;
+}
+
+static void maybeInsertLocation(std::optional<Location> Loc,
+                                const ClangDocContext &CDCtx, Object &Obj) {
+  if (!Loc)
+    return;
+  Location L = *Loc;
+  Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)});
+}
+
+static void extractDescriptionFromInfo(ArrayRef<CommentInfo> Descriptions,
+                                       json::Object &EnumValObj) {
+  if (Descriptions.empty())
+    return;
+  json::Value ArrDesc = Array();
+  json::Array &ADescRef = *ArrDesc.getAsArray();
+  for (const CommentInfo &Child : Descriptions)
+    ADescRef.emplace_back(extractValue(Child));
+  EnumValObj.insert({"EnumValueComments", ArrDesc});
+}
+
+static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
+                                const ClangDocContext &CDCtx) {
+  Object Obj = Object();
+  Obj.insert({"Name", I.Name});
+  Obj.insert({"ID", toHex(toStringRef(I.USR))});
+  Obj.insert({"Access", getAccessSpelling(I.Access).str()});
+  Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
+
+  json::Value ParamArr = Array();
+  for (const auto Val : enumerate(I.Params)) {
+    json::Value V = Object();
+    auto &VRef = *V.getAsObject();
+    VRef.insert({"Name", Val.value().Name});
+    VRef.insert({"Type", Val.value().Type.Name});
+    VRef.insert({"End", Val.index() + 1 == I.Params.size()});
+    ParamArr.getAsArray()->emplace_back(V);
+  }
+  Obj.insert({"Params", ParamArr});
+
+  maybeInsertLocation(I.DefLoc, CDCtx, Obj);
+  return Obj;
+}
+
+static json::Value extractValue(const EnumInfo &I,
+                                const ClangDocContext &CDCtx) {
+  Object Obj = Object();
+  std::string EnumType = I.Scoped ? "enum class " : "enum ";
+  EnumType += I.Name;
+  bool HasComment = std::any_of(
+      I.Members.begin(), I.Members.end(),
+      [](const EnumValueInfo &M) { return !M.Description.empty(); });
+  Obj.insert({"EnumName", EnumType});
+  Obj.insert({"HasComment", HasComment});
+  Obj.insert({"ID", toHex(toStringRef(I.USR))});
+  json::Value Arr = Array();
+  json::Array &ARef = *Arr.getAsArray();
+  for (const EnumValueInfo &M : I.Members) {
+    json::Value EnumValue = Object();
+    auto &EnumValObj = *EnumValue.getAsObject();
+    EnumValObj.insert({"Name", M.Name});
+    if (!M.ValueExpr.empty())
+      EnumValObj.insert({"ValueExpr", M.ValueExpr});
+    else
+      EnumValObj.insert({"Value", M.Value});
+
+    extractDescriptionFromInfo(M.Description, EnumValObj);
+    ARef.emplace_back(EnumValue);
+  }
+  Obj.insert({"EnumValues", Arr});
+
+  extractDescriptionFromInfo(I.Description, Obj);
+  maybeInsertLocation(I.DefLoc, CDCtx, Obj);
+
+  return Obj;
+}
+
+static void extractScopeChildren(const ScopeChildren &S, Object &Obj,
+                                 StringRef ParentInfoDir,
+                                 const ClangDocContext &CDCtx) {
+  json::Value ArrNamespace = Array();
+  for (const Reference &Child : S.Namespaces)
+    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
+
+  if (!ArrNamespace.getAsArray()->empty())
+    Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
+
+  json::Value ArrRecord = Array();
+  for (const Reference &Child : S.Records)
+    ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
+
+  if (!ArrRecord.getAsArray()->empty())
+    Obj.insert({"Record", Object{{"Links", ArrRecord}}});
+
+  json::Value ArrFunction = Array();
+  json::Value PublicFunction = Array();
+  json::Value ProtectedFunction = Array();
+  json::Value PrivateFunction = Array();
+
+  for (const FunctionInfo &Child : S.Functions) {
+    json::Value F = extractValue(Child, ParentInfoDir, CDCtx);
+    AccessSpecifier Access = Child.Access;
+    if (Access == AccessSpecifier::AS_public)
+      PublicFunction.getAsArray()->emplace_back(F);
+    else if (Access == AccessSpecifier::AS_protected)
+      ProtectedFunction.getAsArray()->emplace_back(F);
+    else
+      ArrFunction.getAsArray()->emplace_back(F);
+  }
+
+  if (!ArrFunction.getAsArray()->empty())
+    Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
+
+  if (!PublicFunction.getAsArray()->empty())
+    Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
+
+  if (!ProtectedFunction.getAsArray()->empty())
+    Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
+
+  json::Value ArrEnum = Array();
+  auto &ArrEnumRef = *ArrEnum.getAsArray();
+  for (const EnumInfo &Child : S.Enums)
+    ArrEnumRef.emplace_back(extractValue(Child, CDCtx));
+
+  if (!ArrEnumRef.empty())
+    Obj.insert({"Enums", Object{{"Obj", ArrEnum}}});
+
+  json::Value ArrTypedefs = Array();
+  auto &ArrTypedefsRef = *ArrTypedefs.getAsArray();
+  for (const TypedefInfo &Child : S.Typedefs)
+    ArrTypedefsRef.emplace_back(extractValue(Child));
+
+  if (!ArrTypedefsRef.empty())
+    Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs}}});
+}
+
 static json::Value extractValue(const NamespaceInfo &I,
                                 const ClangDocContext &CDCtx) {
   Object NamespaceValue = Object();
+  std::string InfoTitle = I.Name.empty() ? "Global Namespace"
+                                         : (Twine("namespace ") + I.Name).str();
+
+  StringRef BasePath = I.getRelativeFilePath("");
+  NamespaceValue.insert({"NamespaceTitle", InfoTitle});
+  NamespaceValue.insert({"NamespacePath", BasePath});
+
+  extractDescriptionFromInfo(I.Description, NamespaceValue);
+  extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
   return NamespaceValue;
 }
 
 static json::Value extractValue(const RecordInfo &I,
                                 const ClangDocContext &CDCtx) {
   Object RecordValue = Object();
+  extractDescriptionFromInfo(I.Description, RecordValue);
+  RecordValue.insert({"Name", I.Name});
+  RecordValue.insert({"FullName", I.FullName});
+  RecordValue.insert({"RecordType", getTagType(I.TagType)});
+
+  maybeInsertLocation(I.DefLoc, CDCtx, RecordValue);
+
+  StringRef BasePath = I.getRelativeFilePath("");
+  extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
+  json::Value PublicMembers = Array();
+  json::Array &PubMemberRef = *PublicMembers.getAsArray();
+  json::Value ProtectedMembers = Array();
+  json::Array &ProtMemberRef = *ProtectedMembers.getAsArray();
+  json::Value PrivateMembers = Array();
+  json::Array &PrivMemberRef = *PrivateMembers.getAsArray();
+  for (const MemberTypeInfo &Member : I.Members) {
+    json::Value MemberValue = Object();
+    auto &MVRef = *MemberValue.getAsObject();
+    MVRef.insert({"Name", Member.Name});
+    MVRef.insert({"Type", Member.Type.Name});
+    extractDescriptionFromInfo(Member.Description, MVRef);
+
+    if (Member.Access == AccessSpecifier::AS_public)
+      PubMemberRef.emplace_back(MemberValue);
+    else if (Member.Access == AccessSpecifier::AS_protected)
+      ProtMemberRef.emplace_back(MemberValue);
+    else if (Member.Access == AccessSpecifier::AS_private)
+      ProtMemberRef.emplace_back(MemberValue);
+  }
+  if (!PubMemberRef.empty())
+    RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
+  if (!ProtMemberRef.empty())
+    RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
+  if (!PrivMemberRef.empty())
+    RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+
   return RecordValue;
 }
 
 static void setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
-                               Info *I) {}
-
+                               Info *I) {
+}
 Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
                                                 const ClangDocContext &CDCtx) {
   switch (I->IT) {

Copy link

github-actions bot commented May 1, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from 5af67bc to 888f545 Compare May 6, 2025 21:33
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch from 77bb241 to 516f2b2 Compare May 6, 2025 21:33
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from 888f545 to e215c97 Compare May 6, 2025 22:51
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch from 516f2b2 to 50755c0 Compare May 6, 2025 22:51
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from e215c97 to d832772 Compare May 7, 2025 01:59
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch from 50755c0 to 1ef8e3c Compare May 7, 2025 01:59
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch 2 times, most recently from e66d950 to 8a5872d Compare May 7, 2025 03:23
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch 2 times, most recently from 0ec149d to f8205f7 Compare May 7, 2025 03:25
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch 2 times, most recently from 0e2e6ad to ac8c55e Compare May 7, 2025 03:26
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch from f8205f7 to 1079b7d Compare May 7, 2025 03:26
@ilovepi ilovepi requested a review from evelez7 May 13, 2025 23:16
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from 0e6ab7d to c7f6fb8 Compare May 16, 2025 23:38
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch from 6d311cf to fb0db52 Compare May 16, 2025 23:38
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from c7f6fb8 to fa76fb7 Compare May 17, 2025 00:14
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch 2 times, most recently from e918ff1 to 442fe2f Compare May 17, 2025 00:32
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from fa76fb7 to d0754f4 Compare May 17, 2025 00:33
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch from 442fe2f to 3d87071 Compare May 17, 2025 05:42
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from d0754f4 to 8e0b4fc Compare May 17, 2025 05:42
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from 8e0b4fc to 51bd30a Compare May 20, 2025 18:26
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch from 3d87071 to 7f91d87 Compare May 20, 2025 18:26
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from 51bd30a to c97f7af Compare May 20, 2025 21:05
Copy link
Member

@evelez7 evelez7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just array naming nits.

@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from c97f7af to 6880c2f Compare May 22, 2025 21:18
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-templete-helper branch from 7f91d87 to 86dc2be Compare May 22, 2025 21:18
Base automatically changed from users/ilovepi/clang-doc-mustache-templete-helper to main May 22, 2025 21:20
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch 2 times, most recently from afa7c63 to 03f3e44 Compare May 23, 2025 21:58
Copy link
Contributor Author

ilovepi commented May 23, 2025

Merge activity

  • May 23, 10:52 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 23, 10:55 PM UTC: Graphite rebased this pull request as part of a merge.
  • May 23, 10:56 PM UTC: @ilovepi merged this pull request with Graphite.

Split from #133161. This patch provides the implementation of a number
of extractValue overloads used with the different types of Info.

The new helper functions extract the relevant information from the
different *Infos and inserts them into the correct fields of the JSON
values that will be used with the specific Mustache templates, which
will land separately.

Co-authored-by: Peter Chou <[email protected]>
@ilovepi ilovepi force-pushed the users/ilovepi/clang-doc-mustache-json branch from 03f3e44 to 0c7122c Compare May 23, 2025 22:54
@ilovepi ilovepi merged commit a8be7a7 into main May 23, 2025
6 of 11 checks passed
@ilovepi ilovepi deleted the users/ilovepi/clang-doc-mustache-json branch May 23, 2025 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants